#include "avr-spec.h"
#include "operations.h"

AVRSpec::AVRSpec (istream& is, const string& part)
{
  _errno = None;
  _line = 0;
  _sram_size = 0;
  _sram_map = 0;
  _flash_size = 0;
  _flash_reg_map = -1;

  is.seekg (0, ios::beg);

  string line;

  while (_part_name != part) {
    get_next_line (is, line);

    while (line[0] != '[' && !is.eof ())
      get_next_line (is, line);

    if (is.eof ()) {
      _errno = PartNotFound;
      return;
    }

    int end = line.find_last_of (']');
    if (end < 1) {
      _errno = FileFormatError;
      return;
    }

    _part_name = line.substr (1, end - 1);
  }

  get_next_line (is, line);
  while (line[0] != '[' && !is.eof ()) {
    if (line.size () > 0 && line[0] != '#') {
      if (!parse_option (line))
        return;
    }

    get_next_line (is, line);
  }
}

void AVRSpec::get_next_line (istream& is, string& str)
{
  str.clear ();
  getline (is, str);
  ++_line;
  while (str[str.size () - 1] == '\\') {
    string tmp;
    getline (is, tmp);
    ++_line;
    str.resize (str.size () - 1);
    str += tmp;
  }
}

bool AVRSpec::parse_option (const string& opt_line)
{
  int delim = opt_line.find_first_of ('=');
  if (delim == -1) {
    _errno = FileFormatError;
    return false;
  }

  list<string> opt = string_split (opt_line, '=');
  opt = string_strip (opt);

  if (opt.size () != 2) {
    _errno = FileFormatError;
    return false;
  }

  string& option = opt.front ();
  string& value = opt.back ();

  if (option == "sram_size")
    _sram_size = get_integral_from_string<int> (value);
  else if (option == "sram_map")
    _sram_map = get_integral_from_string<int> (value);
  else if (option == "flash_size")
    _flash_size = get_integral_from_string<int> (value);
  else if (option == "flash_reg_map")
    _flash_reg_map = get_integral_from_string<int> (value);
  else if (option == "operations") {
    list<string> ops = string_split (value, ',');
    ops = string_strip (ops);

    list<string>::const_iterator beg = ops.begin ();
    list<string>::const_iterator end = ops.end ();

    for (; beg != end; ++beg) {
      CoreOperation::OP op = CoreOperation::op_from_string (*beg);

      if (op == CoreOperation::INVALID) {
        _errno = InvalidOptionValue;
        return false;
      }

      _ops.push_back (op);
    }
  }
  else {
    _errno = InvalidOption;
    return false;
  }

  return true;
}

list<string> string_split (const string& str, char delim)
{
  list<string> ret;
  unsigned pos = 0;

  while (pos < str.size ()) {
    int pdelim = str.find_first_of (delim, pos);

    if (pdelim == -1) {
      ret.push_back (str.substr (pos));
      break;
    }
    else {
      ret.push_back (str.substr (pos, pdelim - pos));
      pos = pdelim + 1;
    }
  }

  return ret;
}

string string_strip (const string& str)
{
  int beg = str.find_first_not_of ("\t\n ");
  int end = str.find_last_not_of ("\t\n ") + 1;
  return str.substr (beg, end);
}

list<string> string_strip (const list<string>& strs)
{
  list<string> ret;
  list<string>::const_iterator beg = strs.begin ();
  list<string>::const_iterator end = strs.end ();

  for (; beg != end; ++beg)
    ret.push_back (string_strip (*beg));

  return ret;
}
